﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Net.Sockets;
using System.Net;

namespace TransportAbstractionLibrary
{
    public abstract class ConnectionAccepter
    {
        SocketAsyncEventArgs args;
        Socket listener = null;
        int backlog = 128;
        bool running;

        int port;
        protected int Port
        {
            get { return port; }
        }

        public ConnectionAccepter(int port)
        {
            this.port = port;
        }

        object locker = new object();

        protected void StartListening()
        {
            lock(locker)
            {
                if (!running)
                {
                    running = true;
                }
                else
                {
                    throw new ArgumentException("Is already listening!");
                }
            }
            
            listener = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
            listener.Bind(new IPEndPoint(IPAddress.Any, port));
            listener.Listen(backlog);
            args = new SocketAsyncEventArgs();
            args.Completed += new EventHandler<SocketAsyncEventArgs>(SocketAccepted);
            StartAccept();
        }

        protected void StopListening()
        {
            lock(locker)
            {
                if (running)
                {
                    listener.Close();
                    args.Dispose();
                    args = null;
                    listener = null;
                    running = false;
                }
                else
                {
                    throw new ArgumentException("Is not listening!");
                }
            }
        }

        private void StartAccept()
        {
            lock(locker)
            {
                if (!running)
                {
                    throw new ArgumentException("Is not listening!");
                }
            }
            args.AcceptSocket = null;
            if (!listener.AcceptAsync(args))
            {
                SocketAccepted(args);
            }
        }

        private void SocketAccepted(object sender, SocketAsyncEventArgs e)
        {
            SocketAccepted(e);
        }

        private void SocketAccepted(SocketAsyncEventArgs e)
        {
            SocketError error = e.SocketError;

            if (e.SocketError == SocketError.OperationAborted)
            {
                return; //Server was stopped 
            }

            if (e.SocketError == SocketError.Success)
            {
                Socket handler = e.AcceptSocket;
                InboundConnection(handler);
            }
            StartAccept();
        }

        protected abstract void InboundConnection(Socket socket);
    }
}
